library IEEE;
use work.all;
use work.prims.all;

entity mem_controller is
	port (	CLK, RESET, START, STOP		:	in Bit;
			DELAY						:	in bit_vector(3 downto 0);
			READ, WRITE					:	out Bit;
			WADDR, RADDR				:	out bit_vector(3 downto 0));
end mem_controller;

architecture algorithmic of mem_controller is
begin
	CON: process
		variable iWADDR, iRADDR	:	bit_vector(3 downto 0);
	begin
	  wait on RESET until RESET = '1';
	  iWADDR := "0000";
	  iRADDR := "0000";
		--waiting for START
		READ <= '0';
		WRITE <= '0';
		wait on START until START = '1';
		iWADDR := "0000";
		iRADDR := DELAY;
		WRITE <= '1';
		--dec RADDR, inc WADDR
		while (intval(iRADDR) > 0) loop
			iWADDR := inc(iWADDR);
			iRADDR := dec(iRADDR);
			WADDR <= iWADDR;
			RADDR <= iRADDR;
			wait on CLK until CLK = '0';
			wait on CLK until CLK = '1';
		end loop;
		READ <= '1';
		--inc RADDR, inc WADDR
		while (STOP = '0') loop
			iWADDR := inc(iWADDR);
			iRADDR := inc(iRADDR);
			WADDR <= iWADDR;
			RADDR <= iRADDR;
			wait on CLK until CLK = '0';
			wait on CLK until CLK = '1';
		end loop;
		WRITE <= '0';
		--stopping, inc RADDR, no change in WADDR
		while (iRADDR /= iWADDR) loop
			iRADDR := inc(iRADDR);
			RADDR <= iRADDR;
			wait on CLK until CLK = '0';
			wait on CLK until CLK = '1';
		end loop;
		READ <= '0';
	end process;  
end algorithmic;

architecture dataflow of mem_controller is
  component mux4to1_1
    port (  I0, I1, I2, I3  :   in bit;
            S               :   in bit_vector(1 downto 0);
            O               :   out bit);
  end component;
  component dff_r_en4
    port (D       :   in bit_vector(3 downto 0);
          R, E    :   in bit;
          CLK     :   in bit;
          Q       :   out bit_vector(3 downto 0));
  end component;
  component dff_r4
    port (D       :   in bit_vector(3 downto 0);
          R       :   in bit;
          CLK     :   in bit;
          Q       :   out bit_vector(3 downto 0));
  end component;
  component dff_r_en2
    port (D       :   in bit_vector(1 downto 0);
          R, E    :   in bit;
          CLK     :   in bit;
          Q       :   out bit_vector(1 downto 0));
  end component;
  component inc2
    port (  I   :   in bit_vector(1 downto 0);
            O   :   out bit_vector(1 downto 0));
  end component;
  component mux2to1_1
    port (I0, I1    : in bit;
          S         : in bit;
          O         : out bit);
  end component;
  component mux2to1_4
    port (I0, I1     :  in bit_vector(3 downto 0);
          S          :  in bit;
          O          :  out bit_vector(3 downto 0));
  end component;
  component incdec4
    port (  A   : in  bit_vector(3 downto 0);
            D   : in  bit;
            O   : out bit_vector(3 downto 0));
  end component;
  
  signal state        : bit_vector(1 downto 0);
  signal nstate       : bit_vector(1 downto 0);
  signal EnState      : bit;
  signal EnW          : bit;
  signal RDir         : bit;
  signal ADR          : bit;
  signal naddrR, naddrW : bit_vector(3 downto 0);
  signal addrR, addrW : bit_vector(3 downto 0);
  signal loadR        : bit_vector(3 downto 0);
  signal RZero        : bit;
  signal ReqW         : bit;
  signal X            : bit_vector(3 downto 0);
begin
  RZero <= not (addrR(3) or addrR(2) or addrR(1) or not addrR(0));
  X <= addrR xor addrW;
  ReqW <= not (X(3) or X(2) or X(1) or X(0));
  
  EnW <= state(1) xor state(0);
  RDir <= not state(1);
  READ <= state(1);
  Write <= state(1) xor state(0);
  ADR <= not (state(1) or state(0));
  RADDR <= addrR;
  WADDR <= addrW;
  
  --state control
  DFFS: dff_r_en2 port map(nstate, RESET, EnState, CLK, state);
  INC:  inc2      port map(state, nstate);
  M41:  mux4to1_1 port map(START, RZero, STOP, ReqW, state, EnState);

  --RADDR control
  DFFR: dff_r4    port map(loadR, '0', CLK, addrR);
  ID4R: incdec4   port map(addrR, RDir, naddrR);
  M24:  mux2to1_4 port map(naddrR, DELAY, ADR, loadR);

  --WADDR control
  DFFW: dff_r_en4 port map(naddrW, ADR, EnW, CLK, addrW);
  ID4W: incdec4   port map(addrW, '0', naddrW);
end dataflow;

use work.prims.all;

entity memory_unit is
	port (	CLK, READ, WRITE		:	in Bit;
			DATA_IN, RADDR, WADDR	:	in bit_vector(3 downto 0);
			DATA_OUT				:	out bit_vector(3 downto 0));
end memory_unit;

architecture algorithmic of memory_unit is
	type MEMORY is array(0 to 15) of bit_vector(3 downto 0);
begin
	process (CLK)
		variable MEM: MEMORY;
	begin
		if (WRITE = '1') then
			MEM(intval(WADDR)) := DATA_IN;
		end if;
		if (READ = '1') then
			if (WRITE = '0' OR WADDR /= RADDR) then
				DATA_OUT <= MEM(intval(RADDR));
			end if;
		end if;
	end process;
end algorithmic;

entity VDU is
	port (	CLK, RESET, START, STOP	:	in Bit;
			DELAY, DATA_IN			:	in bit_vector(3 downto 0);
			DATA_OUT				:	out bit_vector(3 downto 0));
end VDU;



architecture dataflow of VDU is
	signal WADDR, RADDR	: bit_vector(3 downto 0);
	signal READ, WRITE	: Bit;

	component mem_controller
		port (	CLK, RESET, START, STOP		:	in Bit;
			DELAY						:	in bit_vector(3 downto 0);
			READ, WRITE					:	out Bit;
			WADDR, RADDR				:	out bit_vector(3 downto 0));
	end component;
	for all: mem_controller use entity work.mem_controller(algorithmic);
	component memory_unit
		port (	CLK, READ, WRITE		:	in Bit;
			DATA_IN, RADDR, WADDR	:	in bit_vector(3 downto 0);
			DATA_OUT				:	out bit_vector(3 downto 0));
	end component;
begin
	
	MC: mem_controller 	port map(CLK, RESET, START, STOP, DELAY, READ, WRITE, WADDR, RADDR);
	MU: memory_unit		port map(CLK, READ, WRITE, DATA_IN, RADDR, WADDR, DATA_OUT);
end dataflow;

